#region File Description
//-----------------------------------------------------------------------------
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using System;
using Microsoft.Xna.Framework;
using AssetData;
#endregion

namespace Editor
{
    /// <summary>
    /// Helper for objects that want to leave particles behind them as they
    /// move around the world. This emitter implementation solves two related
    /// problems:
    /// 
    /// If an object wants to create particles very slowly, less than once per
    /// frame, it can be a pain to keep track of which updates ought to create
    /// a new particle versus which should not.
    /// 
    /// If an object is moving quickly and is creating many particles per frame,
    /// it will look ugly if these particles are all bunched up together. Much
    /// better if they can be spread out along a line between where the object
    /// is now and where it was on the previous frame. This is particularly
    /// important for leaving trails behind fast moving objects such as rockets.
    /// 
    /// This emitter class keeps track of a moving object, remembering its
    /// previous position so it can calculate the velocity of the object. It
    /// works out the perfect locations for creating particles at any frequency
    /// you specify, regardless of whether this is faster or slower than the
    /// game update rate.
    /// </summary>
    public class ParticleEmitter
    {
        public string ID = "";

        private ParticleSystem particleSystem;
        private float timeBetweenParticles;
        public Vector3 PreviousPosition;
        private float timeLeftOver;
        // To allow the emitter to be create up front but only used when needed
        // attempt to avoid garbage.
        private bool active = false;
        public bool Active
        {
            get { return active; }
        }
        // When the effect will end.  Zero for continuous.
        private TimeSpan effectEndTime;


        /// <summary>
        /// Constructs a new particle emitter object.
        /// </summary>
        public ParticleEmitter()
        {
        }

        public void Activate(ParticleSystem particle, float particlesPerSecond, Vector3 initialPosition)
        {
            Activate("", particle, particlesPerSecond, initialPosition, TimeSpan.Zero);
        }

        /// <summary>
        /// Activate a particle emitter with a fixed duration.
        /// </summary>
        public void Activate(string uniqueID, ParticleSystem particle, float particlesPerSecond, Vector3 initialPosition, TimeSpan endTime)
        {
            ID = uniqueID;

            particleSystem = particle;

            ChangeParticlesPerSecond(particlesPerSecond);

            PreviousPosition = initialPosition;
            active = true;
            effectEndTime = endTime;
        }

        /// <summary>
        /// Speed up or slow down how many particles are emitted per second.
        /// </summary>
        public void ChangeParticlesPerSecond(float perSecond)
        {
            timeBetweenParticles = 1.0f / perSecond;
        }

        /// <summary>
        /// Inactive emitters are available for use by any particle.
        /// </summary>
        public void Deactivate()
        {
            ID = "";
            active = false;
        }

        /// <summary>
        /// Update the emitter without moving it
        /// </summary>
        public void Update(GameTimeMirror gameTime, Random random)
        {
            Update(gameTime, PreviousPosition, random);
        }

        /// <summary>
        /// Updates the emitter, creating the appropriate number of particles
        /// in the appropriate positions.
        /// The random needs to be supplied from the correct thread.
        /// </summary>
        public void Update(GameTimeMirror gameTime, Vector3 newPosition, Random random)
        {
            if (!active)
            {
                return;
            }
            if (effectEndTime != TimeSpan.Zero && gameTime.TotalGameTime > effectEndTime)
            {
                Deactivate();
                return;
            }

            // Work out how much time has passed since the previous update.
            float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;

            if (elapsedTime > 0)
            {
                // Work out how fast we are moving.
                Vector3 velocity = (newPosition - PreviousPosition) / elapsedTime;

                // If we had any time left over that we didn't use during the
                // previous update, add that to the current elapsed time.
                float timeToSpend = timeLeftOver + elapsedTime;
                
                // Counter for looping over the time interval.
                float currentTime = -timeLeftOver;

                // Create particles as long as we have a big enough time interval.
                while (timeToSpend > timeBetweenParticles)
                {
                    currentTime += timeBetweenParticles;
                    timeToSpend -= timeBetweenParticles;

                    // Work out the optimal position for this particle. This will produce
                    // evenly spaced particles regardless of the object speed, particle
                    // creation frequency, or game update rate.
                    float mu = currentTime / elapsedTime;

                    Vector3 position = Vector3.Lerp(PreviousPosition, newPosition, mu);

                    // Create the particle.
                    particleSystem.AddParticle(position, velocity, random);
                }

                // Store any time we didn't use, so it can be part of the next update.
                timeLeftOver = timeToSpend;
            }

            PreviousPosition = newPosition;
        }
    }
}
